package com.ns.school.common.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.ns.school.common.shiro.realm.AuthRealm;
import com.ns.school.common.shiro.redis.RedisCacheManager;
import com.ns.school.common.shiro.redis.RedisManager;
import com.ns.school.common.shiro.redis.RedisSessionDAO;
import jakarta.servlet.Filter;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.lang.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    private static final String code = "2AvVhdsgUs0FSA3SDFAdag==";
    private static final int expireTime = 30;// Session超时时间，(分钟)
    @Value("${spring.data.redis.port}")
    private String redisPort;// redis缓存地址
    @Value("${spring.data.redis.host}")
    private String redisHost;// redis缓存端口
    @Value("${spring.data.redis.database}")
    private int database;// redis数据库索引
    @Value("${spring.data.redis.password}")
    private String password;// redis密码

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("authRealm") AuthRealm authRealm, RedisSessionDAO redisSessionDAO) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager(authRealm, redisSessionDAO));
        bean.setSuccessUrl("/index");
        bean.setSuccessUrl("/home");
        bean.setLoginUrl("/home");
        Map<String, Filter> map = new HashMap<>();
        map.put("authc", new FormAuthenticationFilter());
        bean.setFilters(map);
        // 配置访问权限
        LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/", "anon");
        filterMap.put("/static/**", "anon");
        filterMap.put("/admin", "anon");
        filterMap.put("/home", "anon");
        filterMap.put("/newslist", "anon");
        filterMap.put("/admin/system/user/changePassword", "anon");// 修改密码
        filterMap.put("/admin/system/user/add", "anon");// 注册
        filterMap.put("/appraise/seltch", "anon");
        filterMap.put("/schedulelist", "anon");
        filterMap.put("/admin/index", "anon");
        filterMap.put("/admin/login", "anon");
        filterMap.put("/toLogin", "anon");
        filterMap.put("/getCaptcha", "anon");
        filterMap.put("/anonCtrl/", "anon");
        filterMap.put("/sysRole/test", "anon");
        filterMap.put("/systemLogout", "authc");
        filterMap.put("/**", "authc");
        bean.setFilterChainDefinitionMap(filterMap);
        return bean;
    }

    /**
     * Cache Manager (shiro-redis)
     */
    @Bean
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setPrincipalIdFieldName("id");
        return redisCacheManager;
    }

    /**
     * RedisManager (shiro-redis)
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(redisHost + ":" + redisPort);
        redisManager.setDatabase(database);
        if (StringUtils.isNotEmpty(password)) {
            redisManager.setPassword(password);
        }
        redisManager.setTimeout(expireTime * 60);
        return redisManager;
    }

    @Bean
    public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm, RedisSessionDAO redisSessionDAO) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(authRealm);
        manager.setRememberMeManager(rememberMeManager());
        manager.setSessionManager(sessionManager(redisSessionDAO));
        manager.setCacheManager(redisCacheManager());
        return manager;
    }

    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        rememberMeManager.setCookie(rememberMeCookie());
        rememberMeManager.setCipherKey(Base64.decode(code));
        return rememberMeManager;
    }

    @Bean
    public SimpleCookie rememberMeCookie() {
        // 这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setHttpOnly(true);
        // 记住我有效期长达一周
        cookie.setMaxAge(604800);
        return cookie;
    }

    /**
     * Session ID 生成器
     *
     * @return
     */
    @Bean
    public JavaUuidSessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    /**
     * RedisSessionDAO (shiro-redis)
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        redisSessionDAO.setExpire(expireTime * 60);
        redisSessionDAO.setKeyPrefix("ns_login:");
        return redisSessionDAO;
    }

    @Bean
    public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
        DefaultWebSessionManager manager = new DefaultWebSessionManager();
        // 设置session过期时间为1小时(单位：毫秒)
        manager.setGlobalSessionTimeout(expireTime * 60 * 1000);
        manager.setSessionValidationSchedulerEnabled(true);
        manager.setSessionDAO(redisSessionDAO);
        // 去掉 JSESSIONID
        manager.setSessionIdUrlRewritingEnabled(false);
        return manager;
    }

    /**
     * AOP式方法级权限检查
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    /**
     * 保证实现了Shiro内部lifecycle函数的bean执行
     *
     * @return
     */
    @Bean
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("authRealm") AuthRealm authRealm, RedisSessionDAO redisSessionDAO) {
        SecurityManager manager = securityManager(authRealm, redisSessionDAO);
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(manager);
        return advisor;
    }

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

}
